package common;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.junit.Test;

import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import static org.junit.Assert.*;

public class TestCaseUnit {
    private static String  DATA_TEMPLATE="{\n" +
            " \"id\" : \"IDS60901\",\n" +
            " \"name\" : \"Adelaide (West Terrace / ngayirdapira)\",\n" +
            " \"state\" : \"SA\",\n" +
            " \"time_zone\" : \"CST\",\n" +
            " \"lat\": -34.9,\n" +
            " \"lon\": 138.6,\n" +
            " \"local_date_time\": \"15/04:00pm\",\n" +
            " \"local_date_time_full\": \"20230715160000\",\n" +
            " \"air_temp\": 13.3,\n" +
            "\"apparent_t\": 9.5,\n" +
            " \"cloud\": \"Partly cloudy\",\n" +
            " \"dewpt\": 5.7,\n" +
            " \"press\": 1023.9,\n" +
            " \"rel_hum\": 60,\n" +
            " \"wind_dir\": \"S\",\n" +
            " \"wind_spd_kmh\": 15,\n" +
            " \"wind_spd_kt\": 8\n" +
            "}";

    // test send post request
    @Test
    public void testSendPostRequest() {
        try {
            HashMap resultMap = HttpUtils.sendPostRequest("http://localhost:4567","{}");
            // When sending empty data, the expected code should be 400
            assertEquals("400", resultMap.get("code"));
        } catch (Exception e) {
            e.printStackTrace();
            fail("Test failed due to an exception!");
        }
    }

    //test send empty data
    @Test
    public void testSendEmptyData() {
        try {
            HashMap resultMap = HttpUtils.sendPutRequest("http://localhost:4567","");
            // Does the assertion result meet expectations
            assertEquals("204", resultMap.get("code"));
        } catch (Exception e) {
            e.printStackTrace();
            fail("Test failed due to an exception!");
        }
    }

    //test send data
    @Test
    public void testSendData() {
        try {
            JSONObject jsonObject = JSONObject.parseObject(DATA_TEMPLATE);
            jsonObject.put("timestamp",System.currentTimeMillis()+(int)((Math.random()*10001+10000)));
            HashMap resultMap = HttpUtils.sendPutRequest("http://localhost:4567",jsonObject.toString());

            // Assert that the code is either 200 or 201
            Integer code = Integer.parseInt((String)resultMap.get("code"));
            assertTrue(code == 200 || code == 201);
        } catch (Exception e) {
            e.printStackTrace();
            fail("Test failed due to an exception!");
        }
    }

    //test send error data
    @Test
    public void testSendErrorData() {
        try {
            JSONObject jsonObject = JSONObject.parseObject(DATA_TEMPLATE);
            jsonObject.put("timestamp",System.currentTimeMillis()+(int)((Math.random()*10001+10000)));
            HashMap resultMap = HttpUtils.sendPutRequest("http://localhost:4567","123456");
            // When sending incorrect data, the expected code should be 500
            assertEquals("500", resultMap.get("code"));
        } catch (Exception e) {
            e.printStackTrace();
            fail("Test failed due to an exception!");
        }
    }

    //test retrieve latest data
    @Test
    public void testRetrieveLatestData(){
        try {
            Long timestamp = System.currentTimeMillis();
            JSONObject jsonObject = JSONObject.parseObject(DATA_TEMPLATE);
            String id=jsonObject.getString("id");
            // Send data 21 times to ensure the first data has been removed on the server
            for(int i = 0;i < 21; i++){
                jsonObject.remove(id);
                jsonObject.put("id",id+(i+1));
                jsonObject.put("timestamp",timestamp+i);
                HttpUtils.sendPutRequest("http://localhost:4567",jsonObject.toString());
            }

            HashMap resultMap = HttpUtils.sendGetRequest("http://localhost:4567");
            JSONArray jsonArray = JSONArray.parseArray(resultMap.get("result").toString());

            // Check the timestamp of retrieved data
            // and determine if they are all greater than the timestamp of the first data
            for(int i=0;i<jsonArray.size();i++){
                JSONObject data = jsonArray.getJSONObject(i);
                assertTrue(timestamp<data.getLong("timestamp"));
            }
            System.out.println(jsonArray.size());
        } catch (Exception e) {
            e.printStackTrace();
            fail("Test failed due to an exception!");
        }
    }

    //test get data
    @Test
    public void testGetData() {
        try {
            HashMap resultMap = HttpUtils.sendGetRequest("http://localhost:4567");
            // Assert that the code is 200
            assertEquals("200",resultMap.get("code"));
        } catch (Exception e) {
            e.printStackTrace();
            fail("Test failed due to an exception!");
        }
    }

    //test Simulating cluster sending data
    @Test
    public void testSendRangeDataCluster () {
        int size = 50;
        JSONObject jsonObject = JSONObject.parseObject(DATA_TEMPLATE);
        String id=jsonObject.getString("id");
        ExecutorService executorService= Executors.newFixedThreadPool(20);
        CountDownLatch downLatch=new CountDownLatch(size);
        for (int i = 0; i < size; i++) {
            JSONObject sendData = JSONObject.parseObject(jsonObject.toJSONString());
            sendData.remove(id);
            sendData.put("id",id+(i+1));
            // Simulate cluster requests
            executorService.execute(new TestCase.SendTask(i,sendData,downLatch));
        }
        // Waiting for all cluster data to be sent
        try{
            downLatch.await();
        }catch (InterruptedException e){
            fail("Test failed due to an exception!");
        }
        System.out.println("All data has been sent successfully.");
        executorService.shutdown();
    }

    public static class SendTask implements Runnable{

        private int i;
        private JSONObject jsonObject;
        CountDownLatch downLatch;
        public SendTask(int i, JSONObject jsonObject,CountDownLatch downLatch) {
            this.i = i;
            this.jsonObject = jsonObject;
            this.downLatch=downLatch;
        }

        @Override
        public void run() {
            try {
                // Random timestamp, simulating data requests with different timestamps
                jsonObject.put("timestamp",System.currentTimeMillis()+(int)((Math.random()*10001+10000)));
                System.out.println("node"+Thread.currentThread().getName()+"send data："+jsonObject.toJSONString());
                HttpUtils.sendPutRequest("http://localhost:4567",jsonObject.toJSONString());
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                downLatch.countDown();
                System.out.println("node"+Thread.currentThread().getName()+"Sending data completed\n" +
                        "\n");
            }
        }
    }
}
